# Utility functions and global variables.
from clang.cindex import TranslationUnit
system_header_paths = ['/usr/include/x86_64-linux-gnu', '/usr/include', '/usr/lib/llvm-14/lib/clang/14.0.0/include']

LLVM_LIBRARY_PATH = "/usr/lib/llvm-14/lib"

clang_options = TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD | TranslationUnit.PARSE_INCOMPLETE

# Check if it is a system function declaration
def is_system_function_declaration(cursor):
    if cursor.extent.start.file == None:
        return True
    for sys_path in system_header_paths:
        if sys_path in cursor.extent.start.file.name:
            return True
    return False

def get_code_snippet(code, cursor):
    start = cursor.extent.start
    end = cursor.extent.end
    lines = code[start.line-1:end.line]
    lines = [line.rstrip() for line in lines]
    if len(lines) == 1:
        content = "\n".join(lines)[start.column-1:end.column-1]
    else:
        start_line = lines[0]
        end_line = lines[-1]
        start_code = start_line[start.column-1:]
        end_code = end_line[:end.column-1]
        lines[0] = start_code
        lines[-1] = end_code
        content = "\n".join(lines)
    return content

def get_range_code_snippet(code, start, end):
    lines = code[start.line-1:end.line]
    lines = [line.rstrip() for line in lines]
    if len(lines) == 1:
        content = "\n".join(lines)[start.column-1:end.column-1]
    else:
        start_line = lines[0]
        end_line = lines[-1]
        start_code = start_line[start.column-1:]
        end_code = end_line[:end.column-1]
        lines[0] = start_code
        lines[-1] = end_code
        content = "\n".join(lines)
    return content

def get_code_lines(src_path):
    with open(src_path, "r") as c_file:
        full_code = c_file.readlines()
        return full_code

def print_header_src_content(h_files, c_files):
    for fname, value in h_files.items():
        filename, code = value
        if code:
            print(f"Extract header file {filename}!")

    for fname, value in c_files.items():
        filename, code = value
        if code:
            print(f"Extract source file {filename}!")

def print_symbol_table(symbol_table):
    # 打印符号表
    for category, symbols in symbol_table.items():
        print(f"{category}:")
        if isinstance(symbols, dict):
            for name, members in symbols.items():
                print(f"  {name}: {members}")
        else:
            print(f"  {symbols}")

def print_definitions(definitions):
    for definition in definitions:
        print(definition)

def print_code_snippets(code_snippets):
    for cidx, code_snippet in code_snippets.items():
        print(f"Code Snippet {cidx}: \n{code_snippet}")

def print_h_c_files(h_files, c_files):
    for fname, content in h_files.items():
        print(fname, content[0])

    for fname, content in c_files.items():
        print(fname, content[0])

def fill_cfg_into_code(code_dict, cfg):
    from code_kind import CodeKind
    for idx, code_block in code_dict.items():
        code_name = code_block['name']
        code_kind = code_block['kind']
        for cfg_name, cfg_block in cfg.items():
            if code_name == cfg_name and code_kind == CodeKind.FUNCTION:
                code_block['CFG'] = cfg_block

def convert_ddg_info(ddg_name, ddg_graph):
    output = f"DDG of {ddg_name}:\n"
    output += "Nodes:\n"
    for node in ddg_graph["nodes"]:
        output += f"{node[0]} ({node[1]})\n"

    output += "Edges:\n"
    for edge in ddg_graph["edges"]:
        output += f"Edge from {edge[0]} to {edge[1]}\n"
    return output

def fill_ddg_into_code(code_dict, ddg):
    from code_kind import CodeKind
    for idx, code_block in code_dict.items():
        code_name = code_block['name']
        code_kind = code_block['kind']
        for ddg_name, ddg_graph in ddg.items():
            if code_name == ddg_name and code_kind == CodeKind.FUNCTION:
                code_block['DDG'] = convert_ddg_info(ddg_name, ddg_graph)

# 检查是否存在 NULL 的使用
def has_null_usage(content):
    import re
    pattern = r'\bNULL\b'
    return re.search(pattern, content)

# 检查是否存在 #define NULL ((void*)0)
def has_null_definition(content):
    import re
    pattern = r'^\s*#define\s+NULL\s+\(\(void\*\)\s*0\)\s*$'
    return re.search(pattern, content, re.MULTILINE) is not None

# 创建包含 #define NULL ((void*)0) 的同名文件
def insert_null_definition(file_path):
    with open(file_path, 'r') as file:
        content = file.read()

    if has_null_usage(content) and not has_null_definition(content):
        # insert #define NULL ((void*)0)
        content = '#define NULL ((void*)0)//insert for parsing c code\n' + content

        # write extended file content into new file
        with open(file_path, "w") as extended_file:
            extended_file.write(content)

def remove_null_definition(file_path):
    with open(file_path, "r") as file:
        content = file.readlines()

        first_line = content[0]
        NULL_INSERT_FLAG = False
        if first_line.startswith("#define NULL ((void*)0)//insert for parsing c code"):
            NULL_INSERT_FLAG = True
            content = "".join(content[1:])
        if NULL_INSERT_FLAG:
            with open(file_path, "w") as extended_file:
                extended_file.write(content)

def generate_header_path(include_dirs):
    include_paths = [f"-I{path}" for path in include_dirs]
    return include_paths

def collect_include_paths(src_dir):
    import os
    header_paths = set()
    for root, dirs, files in os.walk(src_dir):
        for _file in files:
            if _file.endswith('.h'):
                header_paths.add(root)
    header_paths = list(header_paths)
    return header_paths

